﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using Pahan.McuFusion.Common;
using Pahan.McuFusion.Spi;

namespace Pahan.McuFusion.FileSpi
{
    internal class FileSpiInterface : ISpiInterface
    {
        public FileSpiInterface(FileSpiInterfaceInfo info)
        {
            this.Info = info;
        }
        
        #region Implementation of IDisposable

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Close();
        }

        #endregion

        #region Implementation of IHardwareInterface

        /// <summary>
        /// Gets the information about the hardware interface.
        /// </summary>
        public HardwareInterfaceInfo Info
        {
            get;
            private set;
        }

        /// <summary>
        /// Opens the hardware interface.
        /// </summary>
        public void Open()
        {
            String dir = Path.Combine(
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
                "File SPI");
            Directory.CreateDirectory(dir);
            String filepath = Path.Combine(dir, DateTime.Now.ToString(@"yyyy-MM-dd HH-mm-ss.\tx\t"));
            writer = new StreamWriter(filepath);
        }

        /// <summary>
        /// Closes the hardware interface.
        /// </summary>
        public void Close()
        {
            if (writer != null)
            {
                writer.Dispose();
            }
        }

        #endregion

        #region Implementation of ISpi

        /// <summary>
        /// Gets or sets the MOSI signal.
        /// </summary>
        public bool Mosi
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the MISO signal.
        /// </summary>
        public bool Miso
        {
            get; 
            private set;
        }

        /// <summary>
        /// Gets or sets the SCK signal.
        /// </summary>
        public bool Sck
        {
            get
            {
                return sck;
            }
            set
            {
                if (!sck && value)
                {
                    data = (data << 1) | (Mosi ? 1 : 0);
                    bitCounter++;
                    if (bitCounter % 8 == 0)
                    {
                        WriteByte((byte)data);
                    }
                }
                else if (sck && !value)
                {
                    Miso = (data & 0x80) != 0;
                }
                sck = value;
            }
        }

        /// <summary>
        /// Gets or sets the RESET signal.
        /// </summary>
        public bool Reset
        {
            get; 
            set;
        }

        /// <summary>
        /// Gets or sets the value indicating whether the SCK signal must be inverted.
        /// </summary>
        public bool InvertSck { get; set; }

        /// <summary>
        /// Gets or sets the value indicating whether the MISO signal must be inverted.
        /// </summary>
        public bool InvertMiso { get; set; }

        /// <summary>
        /// Gets or sets the value indicating whether the MOSI signal must be inverted.
        /// </summary>
        public bool InvertMosi { get; set; }

        /// <summary>
        /// Gets or sets the value indicating whether the RESET signal must be inverted.
        /// </summary>
        public bool InvertReset { get; set; }

        /// <summary>
        /// Gets whether the hardware reset is supported by the interface.
        /// </summary>
        public bool HardwareResetSupported
        {
            get
            {
                return true;
            }
        }

        #endregion

        #region Private Members

        private void WriteByte(byte value)
        {
            if (writer != null)
            {
                if (bitCounter % 32 == 0)
                {
                    writer.WriteLine("{0:X2}", value);
                    bitCounter = 0;
                }
                else
                {
                    writer.Write("{0:X2} ", value);
                }
            }
        }

        private int data;

        private int bitCounter;

        private bool sck;

        private TextWriter writer;

        #endregion
    }
}
